package article

import (
	"booksmart/book/internal/domain"
	"booksmart/book/internal/repository/cache"
	dao "booksmart/book/internal/repository/dao/article"
	logger2 "booksmart/book/pkg/logger"
	"context"
	"github.com/ecodeclub/ekit/slice"
	"time"
)

type ArticleRepository interface {
	Create(ctx context.Context, art domain.Article) (int64, error)
	Update(ctx context.Context, art domain.Article) error
	Sync(ctx context.Context, art domain.Article) (int64, error)
	SyncStatus(ctx context.Context, id int64, authorId int64, status domain.ArticleStatus) error
	List(ctx context.Context, uid int64, offset int, limit int) ([]domain.Article, error)
	GetByID(ctx context.Context, id int64) (domain.Article, error)
	GetPublishedById(ctx context.Context, id int64, uid int64) (domain.Article, error)
}

type CachedArticleRepository struct {
	dao dao.ArticleDAO

	//V1
	author dao.AuthorDAO
	reader dao.ReaderDAO

	cache cache.ArticleCache

	l logger2.LoggerV1
}

func (c *CachedArticleRepository) GetPublishedById(ctx context.Context, id int64, uid int64) (domain.Article, error) {
	//TODO implement me
	panic("implement me")
}

func (c *CachedArticleRepository) GetByID(ctx context.Context, id int64) (domain.Article, error) {
	data, err := c.cache.GetByID(ctx, id)
	if err != nil {
		return domain.Article{}, err
	}
	//return c.toDomain(data), err
	return data, err
}

func (c *CachedArticleRepository) List(ctx context.Context, uid int64, offset int, limit int) ([]domain.Article, error) {

	if offset == 0 && limit <= 100 {
		data, err := c.cache.GetFirstPage(ctx, uid)
		if err == nil {
			c.preCache(ctx, data)
			return data, err
		}
	}

	res, err := c.dao.GetByAuthor(ctx, uid, offset, limit)
	if err != nil {
		return nil, err
	}
	data := slice.Map[dao.Article, domain.Article](res, func(idx int, src dao.Article) domain.Article {
		return c.toDomain(src)
	})

	go func() {
		err := c.cache.SetFirstPage(ctx, uid, data)
		if err != nil {
			c.l.Error("会写缓存失败", logger2.Error(err))
		}
		c.preCache(ctx, data)
	}()

	return data, nil
}

func (c *CachedArticleRepository) SyncStatus(ctx context.Context, id int64, authorId int64, status domain.ArticleStatus) error {
	return c.dao.SyncStatus(ctx, id, authorId, status.ToUint8())
}

func (c *CachedArticleRepository) Sync(ctx context.Context, art domain.Article) (int64, error) {
	var (
		id  = art.Id
		err error
	)
	artn := c.toEntity(art)
	if id > 0 {
		err = c.author.UpdateById(ctx, artn)
	} else {
		id, err = c.author.Insert(ctx, artn)
	}
	if err != nil {
		return id, err
	}
	//操作线上库，保存数据，同步过来
	err = c.reader.Upsert(ctx, artn)
	defer func() {
		err := c.cache.DelFirstPage(ctx, art.Id)
		if err != nil {
			c.l.Error("清除缓存失败", logger2.Error(err))
		}
	}()
	return id, err
}

func NewArticleRepository(dao dao.ArticleDAO, l logger2.LoggerV1) ArticleRepository {
	return &CachedArticleRepository{
		dao: dao,
		l:   l,
	}
}

func (c *CachedArticleRepository) Create(ctx context.Context, art domain.Article) (int64, error) {
	defer func() {
		err := c.cache.DelFirstPage(ctx, art.Id)
		if err != nil {
			c.l.Error("清除缓存失败", logger2.Error(err))
		}
	}()
	return c.dao.Insert(ctx, c.toEntity(art))
}

func (c *CachedArticleRepository) Update(ctx context.Context, art domain.Article) error {
	defer func() {
		err := c.cache.DelFirstPage(ctx, art.Id)
		if err != nil {
			c.l.Error("清除缓存失败", logger2.Error(err))
		}
	}()
	return c.dao.UpdateById(ctx, c.toEntity(art))
}

func (c *CachedArticleRepository) toEntity(art domain.Article) dao.Article {
	return dao.Article{
		Id:       art.Id,
		Title:    art.Title,
		Content:  art.Content,
		AuthorId: art.Author.Id,
		Status:   art.Status.ToUint8(),
	}
}

func (c *CachedArticleRepository) toDomain(art dao.Article) domain.Article {
	return domain.Article{
		Id:      art.Id,
		Title:   art.Title,
		Content: art.Content,
		Author:  domain.Author{Id: art.AuthorId},
		Status:  domain.ArticleStatus(art.Status),
		Utime:   time.UnixMilli(art.Utime),
		Ctime:   time.UnixMilli(art.Ctime),
	}
}

func (c *CachedArticleRepository) preCache(ctx context.Context, data []domain.Article) {
	if len(data) > 0 {
		err := c.cache.Set(ctx, data[0], data[0].Id)
		if err != nil {
			c.l.Error("提前预加载缓存失败", logger2.Error(err))
		}
	}
}
